// -*- mode:objc -*-
// $Id: VT100Screen.m,v 1.122 2003/08/12 06:29:02 sgehrman Exp $
//
/*
 **  VT100Screen.m
 **
 **  Copyright (c) 2002, 2003
 **
 **  Author: Fabian, Ujwal S. Sathyam
 **	     Initial code by Kiichi Kusama
 **
 **  Project: iTerm
 **
 **  Description: Implements the VT100 screen.
 **
 **  This program is free software; you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License as published by
 **  the Free Software Foundation; either version 2 of the License, or
 **  (at your option) any later version.
 **
 **  This program is distributed in the hope that it will be useful,
 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 **  GNU General Public License for more details.
 **
 **  You should have received a copy of the GNU General Public License
 **  along with this program; if not, write to the Free Software
 **  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#import "iTerm.h"
#import "VT100Screen.h"
#import "VT100Typesetter.h"
#import "NSString+Communique.h"
#import "ITTerminal.h"
#import "PTYTextView.h"
#import "charmaps.h"
#import "PTYSession.h"
#import "PTYTask.h"
#import "ITPreferences.h"
//#import "ITTerminalView.h"

@implementation VT100Screen

#define DEFAULT_WIDTH     80
#define DEFAULT_HEIGHT    25
#define DEFAULT_FONTNAME  @"Monaco"
#define DEFAULT_FONTSIZE  10
#define DEFAULT_SCROLLBACK 1000

#define MIN_WIDTH     10
#define MIN_HEIGHT    3

#define TABSIZE     8

#define WIDTH_REAL   (WIDTH + 1)

//#define ISDOUBLEWIDTHCHARACTER(c) ((c)>=0x1000)
#define ISDOUBLEWIDTHCHARACTER(idx) ([_session doubleWidth]&&[[_textBuffer attribute:NSCharWidthAttributeName atIndex:(idx) effectiveRange:nil] intValue]==2)

#define ISDOUBLEWIDTHCHARACTERINLINE(idx, line) ([_session doubleWidth]&&[[line attribute:NSCharWidthAttributeName atIndex:(idx) effectiveRange:nil] intValue]==2)

static NSString *NSBlinkAttributeName=@"NSBlinkAttributeName";
static NSString *NSBlinkColorAttributeName=@"NSBlinkColorAttributeName";

static NSString *NSCharWidthAttributeName=@"NSCharWidthAttributeName";

static unichar spaces[300]={0};

static BOOL PLAYBELL = YES;

+ (NSSize)fontSize:(NSFont *)font
{
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    NSSize sz;
    
    [dic setObject:font forKey:NSFontAttributeName];
    
    sz = [@"A" sizeWithAttributes:dic];

    return NSMakeSize(sz.width,[font defaultLineHeightForFont]);
}

+ (NSSize)requireSizeWithFont:(NSFont *)font
			width:(int)width
		       height:(int)height
{
    NSSize sz;
    
    sz = [VT100Screen fontSize:font];
//    NSLog(@"--------fontsize:%f,%f, %f,%f",sz.width,sz.height);
#if USE_CUSTOM_LAYOUT
    return NSMakeSize((sz.width * width) + 2*[VT100Typesetter lineFragmentPadding], (float) height * sz.height);
#else
    return NSMakeSize(sz.width * (width +2), (float) height * sz.height);
#endif
}

+ (NSSize)requireSizeWithFont:(NSFont *)font
{
    NSSize sz;

    sz = [VT100Screen fontSize:font];

    return sz;
}

+ (NSSize)screenSizeInFrame:(NSRect)frame
		       font:(NSFont *)font
{
    NSSize sz;
    int w, h;

    sz = [VT100Screen fontSize:font];
#if USE_CUSTOM_LAYOUT
    w = (int)(frame.size.width  - 2*[VT100Typesetter lineFragmentPadding])/sz.width;
#else
    w = (int)(frame.size.width / sz.width + 0.5) - 2;
#endif
    h = (int)(frame.size.height / sz.height) ;

    return NSMakeSize(w, h);
}

+ (void)setPlayBellFlag:(BOOL)flag
{
    PLAYBELL = flag;
}

- (id)init
{
    if ((self = [super init]) == nil)
	return nil;

    WIDTH = DEFAULT_WIDTH;
    HEIGHT = DEFAULT_HEIGHT;

    CURSOR_X = CURSOR_Y = 0;
    SAVE_CURSOR_X = SAVE_CURSOR_Y = 0;
    SCROLL_TOP = 0;
    SCROLL_BOTTOM = HEIGHT - 1;

    _textStorage = nil;
    FONT = [[NSFont fontWithName:DEFAULT_FONTNAME size:DEFAULT_FONTSIZE] retain];
    TERMINAL = nil;
    _shell = nil;

    TOP_LINE  = 0;
    scrollbackLines = DEFAULT_SCROLLBACK;
    OLD_CURSOR_INDEX=-1;
    [self clearTabStop];
    
    // set initial tabs
    int i;
    for(i = TABSIZE; i < TABWINDOW; i += TABSIZE)
        tabStop[i] = YES;

    for(i=0;i<300;i++) 
        spaces[i]=' ';
    for(i=0;i<4;i++)
        saveCharset[i]=charset[i]=0;

    _textBuffer=[[NSMutableAttributedString alloc] init];

    _updateIndex=_minIndex=0;
    screenLock= [[NSLock alloc] init];
    screenIsLocked = NO;
    clearingBuffer = NO;
    
    return self;
}

- (id)initWithFrame:(NSRect)frame
{
    if ((self = [super init]) == nil)
		return nil;
	
    _textStorage = nil;
    FONT = [[NSFont fontWithName:DEFAULT_FONTNAME size:DEFAULT_FONTSIZE] retain];
    TERMINAL = nil;
    _shell = nil;
    _textBuffer=[[NSMutableAttributedString alloc] init];
	
	NSSize size = [VT100Screen screenSizeInFrame:frame font: [self tallerFont]];	
	
    WIDTH = size.width;
    HEIGHT = size.height;
	
    CURSOR_X = CURSOR_Y = 0;
    SAVE_CURSOR_X = SAVE_CURSOR_Y = 0;
    SCROLL_TOP = 0;
    SCROLL_BOTTOM = HEIGHT - 1;
	
    TOP_LINE  = 0;
    scrollbackLines = DEFAULT_SCROLLBACK;
    OLD_CURSOR_INDEX=-1;
    [self clearTabStop];
    
    // set initial tabs
    int i;
    for(i = TABSIZE; i < TABWINDOW; i += TABSIZE)
        tabStop[i] = YES;
	
    for(i=0;i<300;i++) 
        spaces[i]=' ';
    for(i=0;i<4;i++)
        saveCharset[i]=charset[i]=0;	
	
    _updateIndex=_minIndex=0;
    screenLock= [[NSLock alloc] init];
    screenIsLocked = NO;
    clearingBuffer = NO;
    
    return self;
}

- (void)dealloc
{
    if([self screenIsLocked])
	[self removeScreenLock];
    [screenLock release];
    screenLock = nil;
    
    [FONT release];

    [_textBuffer release];
    [savedBuffer release];

    [newLineString release];
    newLineString = nil;

    [_textStorage release];    

    [super dealloc];
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"WIDTH %d, HEIGHT %d, CURSOR (%d,%d)",
			WIDTH, HEIGHT, CURSOR_X, CURSOR_Y];
}

- (void)setWidth:(int)width height:(int)height
{
	#if DEBUG_METHOD_TRACE
		NSLog(@"%s(%d):-[VT100Screen setWidth:%d height:%d]", __FILE__, __LINE__, width, height);
	#endif
	
    if (width >= MIN_WIDTH && height >= MIN_HEIGHT)
    {
        WIDTH = width;
        HEIGHT = height;

        CURSOR_X = CURSOR_Y = 0;
        SAVE_CURSOR_X = SAVE_CURSOR_Y = 0;
        SCROLL_TOP = 0;
        SCROLL_BOTTOM = HEIGHT - 1;
    }
}

- (int)countNL
{
	NSString *store=[_textBuffer string];
    return [[store componentsSeparatedByString:@"\n"] count]-1;
}

- (void)trimHeight:(int)newHeight
{
	/*
	int myHeight = [self occurrencesOfString:store];
		NSLog (@"------");
		NSLog (@"%@", store);
		NSLog (@"------");		
		int oldLength = [store length]-1;
		int newLength = [store length]-1;
		while ([store characterAtIndex:newLength] == '\n') {
			//NSLog (@"'%@'", [ store substringWithRange:NSMakeRange(newLength, 1) ]);
			newLength--;
			//HEIGHT--;
		}
		
		NSRange aRange = NSMakeRange(newLength, oldLength-newLength);
	[_textBuffer deleteCharactersInRange:aRange];
	store=[_textBuffer string];	
	myHeight = [self occurrencesOfString:store];
	*/
	//}
}

- (void)resizeWidth:(int)width height:(int)height
{
    int i;
	
	//NSLog(@"%s(%d):-[VT100Screen resizeWidth:%d height:%d]", __FILE__, __LINE__, width, height);
	NSLog(@"HEIGHT:%d TOP_LINE:%d CURSOR_Y:%d SAVE_CURSOR_Y:%d SCROLL_TOP:%d SCROLL_BOTTOM:%d", HEIGHT, TOP_LINE, CURSOR_Y, SAVE_CURSOR_Y, SCROLL_TOP, SCROLL_BOTTOM);	
	NSLog (@"%d", [self countNL]);
	
    if (width >= MIN_WIDTH && height >= MIN_HEIGHT) 
    {	
		if (height<HEIGHT) 
		{		
			NSString *store=[_textBuffer string];
			int length = [store length]-1;
			unichar c = [store characterAtIndex:length];
			
			while (height<HEIGHT && c == '\n'){
				length--;
				HEIGHT--;
				c = [store characterAtIndex:length];
			}
			NSRange aRange = NSMakeRange(length, [store length]-length-1);
			[_textBuffer deleteCharactersInRange:aRange];
		}		
		
		if (height>HEIGHT) 
        {
            for(i=HEIGHT;i<height;i++)
               [_textBuffer appendAttributedString:[self attrString:@"\n" ascii:YES]];
        }
        else if (height<HEIGHT) 
		{
			// couldn't trim all
			TOP_LINE+=HEIGHT-height;
			CURSOR_Y-=HEIGHT-height;
			if (CURSOR_Y<0) 
				CURSOR_Y=0;
			SAVE_CURSOR_Y-=HEIGHT-height;
			if (SAVE_CURSOR_Y<0)
				SAVE_CURSOR_Y=0;
		}	
        
        if (width>=WIDTH) 
        {
            HEIGHT=height;
			WIDTH=width;
        }
        else
        {
            WIDTH = width;
            HEIGHT = height;
            
            for(i=0;i<height;i++) 
                [self trimLine:i];
            
            if (CURSOR_X>=width) 
                CURSOR_X=width-1;
            
            if (SAVE_CURSOR_X>=width) 
                SAVE_CURSOR_X=width-1;
            
            SCROLL_TOP = 0;
            SCROLL_BOTTOM = HEIGHT - 1;
        }
		
		NSLog (@"%d", [self countNL]);	
		//SAVE_CURSOR_Y=0;
		//CURSOR_Y=0;
    }
	
	// should refresh textview/scrollview now!!
	//[_textView refresh];
}

- (int)width
{
    return WIDTH;
}

- (int)height
{
    return HEIGHT;
}

- (unsigned int)scrollbackLines
{
    return scrollbackLines;
}

- (void)setScrollback:(unsigned int)lines;
{
    scrollbackLines=lines;
}

- (void)setSession:(PTYSession *)session
{
    _session=session;
}

- (void)setTerminal:(VT100Terminal *)terminal
{
    TERMINAL = terminal;
    [TERMINAL setScreen: self];
}

- (VT100Terminal *)terminal
{
    return TERMINAL;
}

- (void)setTextView:(PTYTextView *)textView
{
    _textView = textView;
}

- (PTYTextView *)textView
{
    return _textView;
}

- (void)setShellTask:(PTYTask *)shell
{
    _shell = shell;
}

- (PTYTask *)shellTask
{
    return _shell;
}

- (void)setTextStorage:(NSTextStorage *)storage
{
    [_textStorage autorelease];
    _textStorage = [storage retain];
}

- (NSTextStorage *)textStorage
{
    return _textStorage;
}

- (BOOL)blinkingCursor
{
    return blinkingCursor;
}

- (void)setBlinkingCursor:(BOOL)flag
{
    blinkingCursor = flag;
}

- (void)initScreen
{
    int i;

    [TERMINAL initDefaultCharacterAttributeDictionary];
    //_textStorage = [[NSTextStorage alloc] init];

    for(i=0;i<HEIGHT-1;i++)
    {
        //[_textStorage appendAttributedString:[self defaultAttrString:@"\n"]];
        [_textBuffer appendAttributedString:[self defaultAttrString:@"\n"]];
    }
        
    blinkShow=YES;
}
    
- (void)setFont:(NSFont *)font nafont:(NSFont *)nafont
{
    [FONT autorelease];
    FONT = [font copy];
    
    [NAFONT autorelease];
    NAFONT = [nafont copy];
    
    FONT_SIZE = [VT100Screen fontSize:FONT];

    [_textBuffer addAttribute:NSFontAttributeName
                   value:FONT
                   range:NSMakeRange(0, [_textBuffer length])];

    [TERMINAL initDefaultCharacterAttributeDictionary];

    [newLineString release];
    newLineString = nil;
}

- (NSFont *)font
{
    #warning hack, this was nil and causing problems, not sure why, quick hack
    if (!FONT)
        FONT = [[NSFont userFixedPitchFontOfSize:10] retain];
    
    return FONT;
}

- (NSFont *)nafont
{
#warning hack, this was nil and causing problems, not sure why, quick hack
    if (!NAFONT)
        NAFONT = [[NSFont userFixedPitchFontOfSize:10] retain];
    
    return NAFONT;
}

- (NSFont *) tallerFont
{
#warning is this necessary?
//    float a=[VT100Screen fontSize:FONT].height;
//    float b=[VT100Screen fontSize:NAFONT].height;

//    return (a>b)?FONT:NAFONT;
    
    return [self font];
}

- (NSSize) characterSize
{
    return [VT100Screen requireSizeWithFont: [self tallerFont]];
}

- (void)putToken:(VT100TCC)token
{
    static unichar s[300]={0};
    int i;
    NSString *str;

    // If we are in print mode, send to printer.
    if([TERMINAL printToAnsi] == YES && token.type != ANSICSI_PRINT)
    {
		[TERMINAL printToken: token];
		return;
    }
	
	NSLog(@"token.type = %d", token.type);
    
	switch (token.type) {
		// our special code
		case VT100_STRING:
			if ([_session doubleWidth]) 
				[self setDoubleWidthString:token.u.string];
			else 
				[self setASCIIString:token.u.string];
			break;
			case VT100_ASCIISTRING:
				[self setASCIIString:token.u.string];
				break;
		case VT100_UNKNOWNCHAR: break;
		case VT100_NOTSUPPORT: break;
		
		//  VT100 CC
		case VT100CC_ENQ: break;
		case VT100CC_BEL: 
			[self playBell]; 
			break;
		case VT100CC_BS:  
			[self backSpace]; 
			break;
		case VT100CC_HT:  
			[self setTab]; 
			break;
		case VT100CC_LF:
		case VT100CC_VT:
		case VT100CC_FF:
			[self setNewLine]; 
			break;
		case VT100CC_CR:  CURSOR_X = 0; break;
		case VT100CC_SO:  break;
		case VT100CC_SI:  break;
		case VT100CC_DC1: break;
		case VT100CC_DC3: break;
		case VT100CC_CAN:
		case VT100CC_SUB: break;
		case VT100CC_DEL: [self deleteCharacters:1];break;
		
		// VT100 CSI
		case VT100CSI_CPR: break;
		case VT100CSI_CUB: [self cursorLeft:token.u.csi.p[0]]; break;
		case VT100CSI_CUD: [self cursorDown:token.u.csi.p[0]]; break;
		case VT100CSI_CUF: [self cursorRight:token.u.csi.p[0]]; break;
		case VT100CSI_CUP: [self cursorToX:token.u.csi.p[1]
										 Y:token.u.csi.p[0]];
		break;
		case VT100CSI_CUU: [self cursorUp:token.u.csi.p[0]]; break;
		case VT100CSI_DA:   [self deviceAttribute:token]; break;
		case VT100CSI_DECALN:
			if (!s[0]) {
				for (i=0;i<300;i++) s[i]='E';
			}
			str=[NSString stringWithCharacters:s length:WIDTH];
			for(i=0;i<HEIGHT;i++)
				[self setASCIIStringToX:0 Y:i string:str];
			break;
		case VT100CSI_DECDHL: break;
		case VT100CSI_DECDWL: break;
		case VT100CSI_DECID: break;
		case VT100CSI_DECKPAM: break;
		case VT100CSI_DECKPNM: break;
		case VT100CSI_DECLL: break;
		case VT100CSI_DECRC: [self restoreCursorPosition]; break;
		case VT100CSI_DECREPTPARM: break;
		case VT100CSI_DECREQTPARM: break;
		case VT100CSI_DECSC: [self saveCursorPosition]; break;
		case VT100CSI_DECSTBM: [self setTopBottom:token]; break;
		case VT100CSI_DECSWL: break;
		case VT100CSI_DECTST: break;
		case VT100CSI_DSR:  [self deviceReport:token]; break;
		case VT100CSI_ED:   [self eraseInDisplay:token]; break;
		case VT100CSI_EL:   [self eraseInLine:token]; break;
		case VT100CSI_HTS: tabStop[CURSOR_X]=YES; break;
		case VT100CSI_HVP: 
			[self cursorToX:token.u.csi.p[1]
						  Y:token.u.csi.p[0]];
			break;
		case VT100CSI_NEL:
			CURSOR_X=0;
		case VT100CSI_IND:
			if(CURSOR_Y == SCROLL_BOTTOM)
			{
				[self scrollUp];
			}
			else
			{
				CURSOR_Y++;
				if (CURSOR_Y>=HEIGHT) {
					CURSOR_Y=HEIGHT-1;
				}
			}
			break;
		
		case VT100CSI_RI:
			if(CURSOR_Y == SCROLL_TOP)
			{
				[self scrollDown];
			}
			else
			{
				CURSOR_Y--;
				if (CURSOR_Y<0) {
					CURSOR_Y=0;
				}	    
			}
			break;
		
		case VT100CSI_RIS: break;
		case VT100CSI_RM: break;
		case VT100CSI_SCS0: charset[0]=(token.u.code=='0'); break;
		case VT100CSI_SCS1: charset[1]=(token.u.code=='0'); break;
		case VT100CSI_SCS2: charset[2]=(token.u.code=='0'); break;
		case VT100CSI_SCS3: charset[3]=(token.u.code=='0'); break;
		case VT100CSI_SGR:  [self selectGraphicRendition:token]; break;
		case VT100CSI_SM: break;
		case VT100CSI_TBC:
		switch (token.u.csi.p[0]) {
			case 3: [self clearTabStop]; break;
			case 0: tabStop[CURSOR_X]=NO;
		}
		break;
		
		case VT100CSI_DECSET:
		case VT100CSI_DECRST:
			if (token.u.csi.p[0]==3 && [TERMINAL allowColumnMode] == YES) 
			{
				int width = ([TERMINAL columnMode]) ? 132 : 80;
				
				// SNG untested
				[[_session SCREEN] resizeWidth:width height:HEIGHT];
				[[_session shell] setWidth:width height:HEIGHT];
			}
			break;
		
		// ANSI CSI
		case ANSICSI_CHA:
			[self cursorToX: token.u.csi.p[0]];
			break;
		case ANSICSI_VPA:
			[self cursorToX: CURSOR_X Y: token.u.csi.p[0]];
			break;
		case ANSICSI_VPR:
			[self cursorToX: CURSOR_X Y: token.u.csi.p[0]+CURSOR_Y];
			break;
		case ANSICSI_ECH:
			[self setASCIIString:[NSString stringWithCharacters:spaces length:token.u.csi.p[0]<=WIDTH?token.u.csi.p[0]:WIDTH]];
			break;
		
		case STRICT_ANSI_MODE:
			[TERMINAL setStrictAnsiMode: ![TERMINAL strictAnsiMode]];
			break;
		
		case ANSICSI_PRINT:
			if(token.u.csi.p[0] == 4)
				[TERMINAL setPrintToAnsi: NO];
			else if (token.u.csi.p[0] == 5)
				[TERMINAL setPrintToAnsi: YES];
			break;
		
		// XTERM extensions
		case XTERMCC_WIN_TITLE:
		case XTERMCC_WINICON_TITLE:
		case XTERMCC_ICON_TITLE:
			[_session setName:token.u.string];
			break;
		case XTERMCC_INSBLNK: [self insertBlank:token.u.csi.p[0]]; break;
		case XTERMCC_INSLN: [self insertLines:token.u.csi.p[0]]; break;
		case XTERMCC_DELCH: [self deleteCharacters:token.u.csi.p[0]]; break;
		case XTERMCC_DELLN: [self deleteLines:token.u.csi.p[0]]; break;
		
		default:
			NSLog(@"%s(%d): bug?? token.type = %d", 
			  __FILE__, __LINE__, token.type);
			break;
    }
}

- (void)clearBuffer
{
    [_textStorage deleteCharactersInRange:NSMakeRange(0, [_textStorage length])];
    [_textBuffer deleteCharactersInRange:NSMakeRange(0, [_textBuffer length])];
    _updateIndex=0;
    _minIndex=0;
    
    [self clearScreen];
    [self initScreen];
    CURSOR_X = CURSOR_Y = 0;
    clearingBuffer = YES;
}

- (void)clearScrollbackBuffer
{
    int idx=[self getIndexAtX:0 Y:0 withPadding:NO];
    
    [_textStorage deleteCharactersInRange:NSMakeRange(0, idx+_updateIndex)];
    [_textBuffer deleteCharactersInRange:NSMakeRange(0, idx)];
    _updateIndex=0;
    _minIndex=0;

    TOP_LINE = 0;
}

- (void) saveBuffer
{
    [savedBuffer release];
    savedBuffer = [[NSAttributedString alloc] initWithAttributedString: _textBuffer];
}

- (void) restoreBuffer
{
    if([savedBuffer length] > 0)
    {
	[_textBuffer setAttributedString: savedBuffer];
    }
    [savedBuffer release];
    savedBuffer = nil;
}

- (int) getIndexAtX:(int)x Y:(int)y withPadding:(BOOL)padding
{
    NSString *s=[_textBuffer string];
    int len=[s length];
    int idx=len-1;
    
    if (x>=WIDTH||y>=HEIGHT||x<0||y<0) {
        NSLog(@"getIndexAtX: out of bound: x = %d; y = %d, WIDTH = %d; HEIGHT = %d", x, y, WIDTH, HEIGHT);
        return -1;
    }
    for(;y<HEIGHT&&idx>=0;idx--) 
    {
        if ([s characterAtIndex:idx]=='\n')
            y++;
    }
    
    if (y<HEIGHT) idx++; else idx+=2;
    
    for(;x>0&&idx<len&&[s characterAtIndex:idx]!='\n';idx++)
    {
        if (ISDOUBLEWIDTHCHARACTER(idx)) 
            x-=2;
        else 
            x--;
    }
    
    if (x>0)
    {
        [_textBuffer insertAttributedString:[self defaultAttrString:[NSString stringWithCharacters:spaces length:x]] atIndex:idx];
        if (idx<_minIndex) 
            _minIndex=idx;
        idx+=x;
    }

    if (x<0) {
        CURSOR_IN_MIDDLE=YES;
        idx--;
    }
    else 
        CURSOR_IN_MIDDLE=NO;
    
    if (idx<0) {
        NSLog(@"getIndexAtX Error! x:%d, y:%d",x,y);
    }
    if (idx<_minIndex)
        _minIndex=idx;
    
    return idx;
}

- (int) getTVIndex:(int)x y:(int)y
{
    return [self getIndexAtX:x Y:y withPadding:YES] + _updateIndex;
}

- (void)setASCIIString:(NSString *)string
{
    int i,idx,x2;
    BOOL doubleWidth=[_session doubleWidth];
    int j, idx2, len, x;
    
    NSString *s=(charset[[TERMINAL charset]]?[self translate:string]:string);
//    NSLog(@"%d(%d):%@",[TERMINAL charset],charset[[TERMINAL charset]],string);

    if (s==nil) return;
    len = [s length];
    if (len<1) return;

    NSString *store=[_textBuffer string];

    for(idx2=0;idx2<len;) 
    {
         store=[_textBuffer string];
        if (CURSOR_X>=WIDTH) 
        {
            if ([TERMINAL wraparoundMode]) 
            {
                [self setNewLine];
                CURSOR_X=0;

                // mark the position
		NSRange searchRange, aRange;

		if(CURSOR_X >= WIDTH || CURSOR_Y >= HEIGHT)
		{
		    searchRange.location = [[_textBuffer string] length] - 10;
		}
		else
		{
		    searchRange.location = [self getIndexAtX: CURSOR_X Y: CURSOR_Y withPadding: NO] - 10;
		}
		searchRange.length = 10;

		aRange = [[_textBuffer string] rangeOfString: @"\n" options: NSBackwardsSearch range: searchRange];

		if(aRange.length > 0)
		{
		    [_textBuffer addAttribute: @"VT100LineWrap" value: @"YES" range: aRange];
		}		                
            }
            else 
            {
                CURSOR_X=WIDTH-1;
                idx2=len-1;
            }
        }
        if ([TERMINAL insertMode]) 
        {
            if(WIDTH-CURSOR_X<=len-idx2) 
                x=WIDTH;
            else 
                x=CURSOR_X+len-idx2;
            
            j=x-CURSOR_X;
            if (j<=0) 
            {
                //NSLog(@"setASCIIString: output length=0?(%d+%d)%d+%d",CURSOR_X,j,idx2,len);
                break;
            }
            
            [self insertBlank:j];
            idx=[self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];

            [_textBuffer replaceCharactersInRange:NSMakeRange(idx,j)
                         withAttributedString:[self attrString:[s substringWithRange:NSMakeRange(idx2,j)] ascii:YES]];
	    
            CURSOR_X=x;
            idx2+=j;
        }
        else 
        {
            idx=[self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];
            if(WIDTH-CURSOR_X<=len-idx2)
                x=WIDTH;
            else 
                x=CURSOR_X+len-idx2;
            
            j=x-CURSOR_X;
            if (j<=0) 
            {
                //NSLog(@"setASCIIString1: output length=0?(%d+%d)%d+%d",CURSOR_X,j,idx2,len);
                break;
            }

            if (idx>=[store length]) 
            {
                //NSLog(@"setASCIIString: About to append [%@](%d+%d),  (%d)",
                //      [string substringWithRange:NSMakeRange(idx2,j)],idx2,j,[store length]);
                [_textBuffer appendAttributedString:[self attrString:[s substringWithRange:NSMakeRange(idx2,j)]  ascii:YES]];
		
            }
            else if ([store characterAtIndex:idx]=='\n') 
            {
                //NSLog(@"setASCIIString: About to insert [%@](%d+%d),  (%d)",
                //      [string substringWithRange:NSMakeRange(idx2,j)],idx2,j,[store length]);
                [_textBuffer insertAttributedString:[self attrString:[s substringWithRange:NSMakeRange(idx2,j)]  ascii:YES] atIndex:idx];
		
            }
            else 
            {
                if (CURSOR_IN_MIDDLE) 
                {
                    //NSLog(@"setASCIIString: Start from middle of a hanzi");
                    [_textBuffer replaceCharactersInRange:NSMakeRange(idx,1)
                                 withAttributedString:[self attrString:@"??"  ascii:YES]];
		    
                    store=[_textBuffer string];
                    idx++;
                }
                //            NSLog(@"index {%d,%d]->%d",CURSOR_X,CURSOR_Y,idx);
                //NSLog(@"%d+%d->%d",idx2,j,len);
                for(i=0,x2=CURSOR_X;x2<x&&idx+i<[store length]&&[store characterAtIndex:idx+i]!='\n';x2++,i++)
                {
                    if (doubleWidth&&[[_textBuffer attribute:NSCharWidthAttributeName atIndex:(idx+i) effectiveRange:nil] intValue]==2)
                        x2++;
                }
                if (x2>x) 
                {
                    //NSLog(@"setASCIIString: End in the middle of a hanzi");
                    [_textBuffer replaceCharactersInRange:NSMakeRange(idx+i-1,1)
                                 withAttributedString:[self attrString:@"??" ascii:YES]];
		    
                    store=[_textBuffer string];
                }
                
                //NSLog(@"setASCIIString: About to change [%@](%d+%d) ==> [%@](%d+%d)  (%d)",
                //      [store substringWithRange:NSMakeRange(idx,i)],idx,i,
                //      [s substringWithRange:NSMakeRange(idx2,j)],idx2,j,[store length]);
                [_textBuffer replaceCharactersInRange:NSMakeRange(idx,i)
                             withAttributedString:[self attrString:[s substringWithRange:NSMakeRange(idx2,j)]  ascii:YES]];
				
            }
	    
            CURSOR_X=x;
            idx2+=j;
        }
    }

	// TG - hack to detect SSH password	
	if([string rangeOfString:@"assword:"].location != NSNotFound)
		[_session foundString:@"assword:"];
}

- (void)setDoubleWidthString:(NSString *)string
{
    int i,idx,x2;
    int j, idx2, len, x;

    if (string==nil)
        return;
    len = [string length];
    
    if (len<1)
        return;

    NSString *store;

    for(idx2=0;idx2<len;) 
    {
        store=[_textBuffer string];
        if (CURSOR_X>=WIDTH) 
        {
            if ([TERMINAL wraparoundMode]) 
            {
                [_textBuffer addAttribute: @"VT100LineWrap" value: @"YES" range: NSMakeRange([self getIndexAtX:WIDTH-1 Y:CURSOR_Y withPadding:NO],1)];

                [self setNewLine];
                CURSOR_X=0;
            }
            else 
            {
                CURSOR_X=WIDTH-2;
                idx2=len-1;
            }
        }
        if ([TERMINAL insertMode]) 
        {
            if(WIDTH-CURSOR_X<=(len-idx2)*2)
                x=WIDTH;
            else 
                x=CURSOR_X+(len-idx2)*2;
            
            j=(x-CURSOR_X+1)/2;
            if (j<=0) 
            {
                //NSLog(@"setDoubleWidthString: output length=0?(%d+%d)%d+%d",CURSOR_X,j,idx2,len);
                break;
            }
            [self insertBlank:x-CURSOR_X];
            idx=[self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];

            [_textBuffer replaceCharactersInRange:NSMakeRange(idx,x-CURSOR_X)
                         withAttributedString:[self attrString:[string substringWithRange:NSMakeRange(idx2,j)]  ascii:NO]];

            CURSOR_X=x;
            idx2+=j;
        }
        else 
        {
            idx=[self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];
            if(WIDTH-CURSOR_X<=(len-idx2)*2)
                x=WIDTH;
            else 
                x=CURSOR_X+(len-idx2)*2;
            
            j=(x-CURSOR_X+1)/2;
            if (j<=0) 
            {
                //NSLog(@"setDoubleWidthString:1: output length=0?(%d+%d)%d+%d",CURSOR_X,j,idx2,len);
                break;
            }

            if (idx>=[store length]) 
            {
                //NSLog(@"setDoubleWidthString: About to append [%@](%d+%d),(%d)",
                //      [string substringWithRange:NSMakeRange(idx2,j)],idx2,j,[store length]);

                [_textBuffer appendAttributedString:[self attrString:[string substringWithRange:NSMakeRange(idx2,j)]  ascii:NO]];
		
            }
            else if ([store characterAtIndex:idx]=='\n') 
            {
                //NSLog(@"setDoubleWidthString: About to insert [%@](%d+%d),  (%d)",
                //      [string substringWithRange:NSMakeRange(idx2,j)],idx2,j,[store length]);
                [_textBuffer insertAttributedString:[self attrString:[string substringWithRange:NSMakeRange(idx2,j)]  ascii:NO] atIndex:idx];
		
            }
            else 
            {
            if (CURSOR_IN_MIDDLE) 
            {
                //NSLog(@"setDoubleWidthString: Start from middle of a hanzi");
                [_textBuffer replaceCharactersInRange:NSMakeRange(idx,1)
                             withAttributedString:[self attrString:@"??"  ascii:YES]];		
		
                store=[_textBuffer string];
                idx++;
            }
            for(i=0,x2=CURSOR_X;x2<x&&idx+i<[store length]&&[store characterAtIndex:idx+i]!='\n';x2++,i++)
            {
                if (ISDOUBLEWIDTHCHARACTER(idx+i))
                    x2++;
            }
            if (x2>x) 
            {
                //NSLog(@"setDoubleWidthString: End in the middle of a hanzi");
                [_textBuffer replaceCharactersInRange:NSMakeRange(idx+i-1,1)
                             withAttributedString:[self attrString:@"??"  ascii:YES]];		
		
                store=[_textBuffer string];
            }
                //      [store substringWithRange:NSMakeRange(idx,i)],idx,i,
                //      [string substringWithRange:NSMakeRange(idx2,j)],idx2,j,[store length]); 
                [_textBuffer replaceCharactersInRange:NSMakeRange(idx,i)
                             withAttributedString:[self attrString:[string substringWithRange:NSMakeRange(idx2,j)]  ascii:NO]];	    
	    
            }

            CURSOR_X=x;
            idx2+=j;
        }
    }
}
        
- (void)setASCIIStringToX:(int)x
		   Y:(int)y
	      string:(NSString *)string 
{
    int sx, sy;

    sx = CURSOR_X;
    sy = CURSOR_Y;
    CURSOR_X = x;
    CURSOR_Y = y;
    [self setASCIIString:string]; 
    CURSOR_X = sx;
    CURSOR_Y = sy;
}

- (void)setNewLine
{
    if (CURSOR_Y  < SCROLL_BOTTOM || (CURSOR_Y < (HEIGHT - 1) && CURSOR_Y > SCROLL_BOTTOM)) 
    {
        CURSOR_Y++;
    }
    // if top of scrolling area is the same as the screen, add a new line at the bottom of the scrolling area so that
    // the top line goes into the scrollback buffer.
    else if (SCROLL_TOP == 0 && SCROLL_BOTTOM == HEIGHT - 1) {

        if(newLineString == nil)
			newLineString = [[self attrString:@"\n" ascii:YES] retain];
		[_textBuffer appendAttributedString:newLineString];
		TOP_LINE++;
    }
    else 
    {
        [self scrollUp];
    }
}

- (int) topLines
{
    return TOP_LINE;
}

- (void)showCursor;
{
    NSWindow* window = [_textView window];
    
    if (window)
    {
        if ([window isKeyWindow] && ([window firstResponder] == _textView))
            [self updateCursor:YES];
        else
            [self updateCursor:NO];
    }
}

- (void)updateCursor:(BOOL)activate
{
    NSMutableDictionary *dic;

    // Show cursor at new position by reversing foreground/background colors
    if ((CURSOR_X >= 0) && (CURSOR_X < WIDTH) && (CURSOR_Y >= 0) && (CURSOR_Y < HEIGHT))
    {
		int idx = [self getTVIndex:CURSOR_X y:CURSOR_Y];
        
        if (idx >= [[_textStorage string] length])
        {
            [_textStorage appendAttributedString:[self defaultAttrString:@" "]];
            
            // SNG this was causing an exception, so I added this idx was 4 and length was 3
            idx = [[_textStorage string] length] - 1;
        }
        else if ([[_textStorage string] characterAtIndex:idx] == '\n') 
            [_textStorage insertAttributedString:[self defaultAttrString:@" "] atIndex:idx];

        // reverse the video on the position where the cursor is supposed to be shown.
        dic = [NSMutableDictionary dictionaryWithDictionary: [_textStorage attributesAtIndex:idx effectiveRange:nil]];
        
	if (activate)
	{
	    [dic setObject:[TERMINAL defaultFGColor] forKey:NSBackgroundColorAttributeName];
	    [dic setObject:[TERMINAL defaultBGColor] forKey:NSForegroundColorAttributeName];
	}
	else
	{
	    NSColor *aColor = [[TERMINAL defaultBGColor] blendedColorWithFraction: 0.5 ofColor: [TERMINAL defaultFGColor]];
	    if(aColor != nil)
	    {
			[dic setObject: aColor forKey:NSBackgroundColorAttributeName];
			[dic setObject:[TERMINAL defaultBGColor] forKey:NSForegroundColorAttributeName];
	    }
	    else
	    {
			[dic setObject:[TERMINAL defaultFGColor] forKey:NSBackgroundColorAttributeName];
			[dic setObject:[TERMINAL defaultBGColor] forKey:NSForegroundColorAttributeName];
	    }
	}

        [_textStorage setAttributes:dic range:NSMakeRange(idx,1)];
    }
}

- (void)deleteCharacters:(int) n
{
    int width;

    if (CURSOR_X >= 0 && CURSOR_X < WIDTH &&
        CURSOR_Y >= 0 && CURSOR_Y < HEIGHT)
    {
        for(;n>0;n--) 
        {
	    int idx = [self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];

            width = [[_textBuffer attribute:NSCharWidthAttributeName atIndex:(idx) effectiveRange:nil] intValue];
            if (idx<[_textBuffer length]&&[[_textBuffer string] characterAtIndex:idx]!='\n') 
            {
                [_textBuffer deleteCharactersInRange:NSMakeRange(idx, 1)];
                
                if (width==2)  
                    [_textBuffer insertAttributedString:[self attrString:@"?" ascii:YES] atIndex:idx];
            }
            else 
                break;
        }
    }
}

- (void)backSpace
{
    if (CURSOR_X > 0) 
        CURSOR_X--;
}

- (void)setTab
{
    CURSOR_X++; // ensure we go to the next tab in case we are already on one
    for(;!tabStop[CURSOR_X]&&CURSOR_X<WIDTH; CURSOR_X++);
}

- (void)clearScreen
{
    [self resizeWidth:WIDTH height:HEIGHT];
}

- (void)eraseInDisplay:(VT100TCC)token
{
    int x1, y1, x2, y2;
    int y;
    int idx,i;

    NSString *store=[_textBuffer string];

    switch (token.u.csi.p[0]) 
    {
    case 1:
        x1 = 0;
        y1 = 0;
        x2 = CURSOR_X;
        y2 = CURSOR_Y;
        break;

    case 2:
        x1 = 0;
        y1 = 0;
        x2 = WIDTH - 1;
        y2 = HEIGHT - 1;
	
        break;

    case 0:
    default:
        x1 = CURSOR_X;
        y1 = CURSOR_Y;
        x2 = WIDTH - 1;
        y2 = HEIGHT - 1;
        break;
    }

    // if we are clearing the entire screen, move the current screen into the scrollback buffer
    if(x1 == 0 && y1 == 0 && x2 == (WIDTH -1 ) && y2 == (HEIGHT - 1) && clearingBuffer == NO)
    {
	[self setScreenLock];
	[_textStorage beginEditing];
	//NSLog(@"'%@'", [_textBuffer string]);
	if(newLineString == nil)
	    newLineString = [[self attrString:@"\n" ascii:YES] retain];
	[_textStorage appendAttributedString:newLineString];
	TOP_LINE++;
	_updateIndex = [_textStorage length];
	// turn off the cursor
	if(cursorIndex < [_textStorage length])
	{
	    NSMutableDictionary *dic;
	    dic =  [NSMutableDictionary dictionaryWithDictionary: [_textStorage attributesAtIndex:cursorIndex effectiveRange:nil]];
	    [dic setObject:[TERMINAL defaultBGColor] forKey:NSBackgroundColorAttributeName];
	    [dic setObject:[TERMINAL defaultFGColor] forKey:NSForegroundColorAttributeName];
	    [_textStorage setAttributes:dic range:NSMakeRange(cursorIndex,1)];
	}
	[_textStorage endEditing];
	[self removeOverLine];
	[self removeScreenLock];
	[[_session TEXTVIEW] scrollEnd];

	// update TOP_LINE
	for(i = 0; i < [_textBuffer length]; i++)
	{
	    if([[_textBuffer string] characterAtIndex: i] == '\n')
		TOP_LINE++;
	}
    }
    else
	clearingBuffer = NO;

    for (y = y1; y <= y2; ++y ) {
        if (y == y1 && y == y2) {
            if (x2 - x1 > 0)
                [self setASCIIStringToX:x1  Y:y  string:[NSString stringWithCharacters:spaces length:x2 - x1+1]];
        }
        else if (y == y1) {
            i=idx=[self getIndexAtX:x1 Y:y withPadding:YES];

            for(;i<[store length]&&[store characterAtIndex:i]!='\n';i++);
            
            if (i>idx) 
                [_textBuffer deleteCharactersInRange:NSMakeRange(idx,i-idx)];
        }
        else if (y == y2) {
            [self setASCIIStringToX:0  Y:y  string:[NSString stringWithCharacters:spaces length:x2+1]];
        }
        else {
            i=idx=[self getIndexAtX:0 Y:y withPadding:YES];

            for(;i<[store length]&&[store characterAtIndex:i]!='\n';i++);
           // NSLog(@"whole line %d(%d,%d)",y,idx,i);
            if (i>idx) [_textBuffer deleteCharactersInRange:NSMakeRange(idx,i-idx)];
        }
    }
}

- (void)eraseInLine:(VT100TCC)token
{
    int i, idx;
    NSString *s;
    
    switch (token.u.csi.p[0]) {
    case 1:
        [self setASCIIStringToX:0  Y:CURSOR_Y  string:[NSString stringWithCharacters:spaces length:CURSOR_X+1]];
        break;
    case 2:
	CURSOR_X = 0;
	// continue, next case....

    case 0:
        i=idx=[self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];

        s=[_textBuffer string];
        for(;i<[s length]&&[s characterAtIndex:i]!='\n';i++);
        if (i>idx) 
            [_textBuffer deleteCharactersInRange:NSMakeRange(idx,i-idx)];
        break;
    default:
        ;
    }
}

- (void)selectGraphicRendition:(VT100TCC)token
{
}

- (void)cursorLeft:(int)n
{
    int x = CURSOR_X - (n>0?n:1);

    if (x < 0)
	x = 0;
    if (x >= 0 && x < WIDTH)
	CURSOR_X = x;
}

- (void)cursorRight:(int)n
{
    int x = CURSOR_X + (n>0?n:1);

    if (x >= WIDTH)
	x =  WIDTH - 1;
    if (x >= 0 && x < WIDTH)
	CURSOR_X = x;
}

- (void)cursorUp:(int)n
{
    int y = CURSOR_Y - (n>0?n:1);

    if(CURSOR_Y >= SCROLL_TOP)
	CURSOR_Y=y<SCROLL_TOP?SCROLL_TOP:y;
    else
	CURSOR_Y = y;
}

- (void)cursorDown:(int)n
{
    int y = CURSOR_Y + (n>0?n:1);

    if(CURSOR_Y <= SCROLL_BOTTOM)
	CURSOR_Y=y>SCROLL_BOTTOM?SCROLL_BOTTOM:y;
    else
	CURSOR_Y = y;
}

- (void) cursorToX: (int) x
{
    int x_pos;
    
    x_pos = (x-1);

    if(x_pos < 0)
		x_pos = 0;
    else if(x_pos >= WIDTH)
		x_pos = WIDTH - 1;

    CURSOR_X = x_pos;
	
}

- (void)cursorToX:(int)x Y:(int)y
{
    int x_pos, y_pos;

    x_pos = x - 1;
    y_pos = y - 1;

    if ([TERMINAL originMode]) y_pos += SCROLL_TOP;

    if(x_pos < 0)
	x_pos = 0;
    else if(x_pos >= WIDTH)
	x_pos = WIDTH - 1;
    if(y_pos < 0)
	y_pos = 0;
    else if(y_pos >= HEIGHT)
	y_pos = HEIGHT - 1;

    CURSOR_X = x_pos;
    CURSOR_Y = y_pos;
	
	//[self updateCursor:YES];
}

- (void)saveCursorPosition
{
    int i;

    if (CURSOR_X >= 0 && CURSOR_X < WIDTH)
        NSParameterAssert(CURSOR_X >= 0 && CURSOR_X < WIDTH);
    
    NSParameterAssert(CURSOR_Y >= 0 && CURSOR_Y < HEIGHT);
    
    SAVE_CURSOR_X = CURSOR_X;
    SAVE_CURSOR_Y = CURSOR_Y;

    for(i=0;i<4;i++) 
        saveCharset[i]=charset[i];
}

- (void)restoreCursorPosition
{
    int i;

    CURSOR_X = SAVE_CURSOR_X;
    CURSOR_Y = SAVE_CURSOR_Y;

    for(i=0;i<4;i++)
        charset[i]=saveCharset[i];
    
    NSParameterAssert(CURSOR_X >= 0 && CURSOR_X < WIDTH);
    NSParameterAssert(CURSOR_Y >= 0 && CURSOR_Y < HEIGHT);
}

- (void)setTopBottom:(VT100TCC)token
{
    int top, bottom;

    top = token.u.csi.p[0] == 0 ? 0 : token.u.csi.p[0] - 1;
    bottom = token.u.csi.p[1] == 0 ? HEIGHT - 1 : token.u.csi.p[1] - 1;
    if (top >= 0 && top < HEIGHT &&
        bottom >= 0 && bottom < HEIGHT &&
        bottom >= top)
    {
        SCROLL_TOP = top;
        SCROLL_BOTTOM = bottom;

	if ([TERMINAL originMode]) {
	    CURSOR_X = 0;
	    CURSOR_Y = SCROLL_TOP;
	}
	else {
	    CURSOR_X = 0;
	    CURSOR_Y = 0;
	}
    }
}

- (void)scrollUp
{
    int idx, idx2;
    NSRange aRange;

    NSParameterAssert(SCROLL_TOP >= 0 && SCROLL_TOP < HEIGHT);
    NSParameterAssert(SCROLL_BOTTOM >= 0 && SCROLL_BOTTOM < HEIGHT);
    NSParameterAssert(SCROLL_TOP <= SCROLL_BOTTOM );
    
    //NSLog(@"SCROLL-UP[%d-%d]; Y = %d",SCROLL_TOP,SCROLL_BOTTOM, CURSOR_Y);
    idx=[self getIndexAtX:0 Y:SCROLL_TOP withPadding:YES];
    if (SCROLL_TOP==HEIGHT-1) idx2=[_textBuffer length];
    else idx2=[self getIndexAtX:0 Y:SCROLL_TOP+1 withPadding:YES];
    aRange = NSMakeRange(idx,idx2-idx);
    if(aRange.length <= 0)
        aRange.length = 1;

    // if we are at the top of the screen, save the line in the scrollback buffer, otherwise delete it.
    if(SCROLL_TOP != 0)
	[_textBuffer deleteCharactersInRange:aRange];
    else
	TOP_LINE++;
    
    if (SCROLL_BOTTOM>=HEIGHT-1) {
	if(newLineString == nil)
	    newLineString = [[self attrString:@"\n" ascii:YES] retain];
        [_textBuffer appendAttributedString:newLineString];
    }
    else if(CURSOR_Y <= SCROLL_BOTTOM)
    {
        idx=[self getIndexAtX:0 Y:SCROLL_BOTTOM+1 withPadding:YES];
	if(newLineString == nil)
	    newLineString = [[self attrString:@"\n" ascii:YES] retain];
        [_textBuffer insertAttributedString:newLineString atIndex:idx];
    }
}

- (void)scrollDown
{
    int idx, idx2;
    NSRange aRange;
    
    NSParameterAssert(SCROLL_TOP >= 0 && SCROLL_TOP < HEIGHT);
    NSParameterAssert(SCROLL_BOTTOM >= 0 && SCROLL_BOTTOM < HEIGHT);
    NSParameterAssert(SCROLL_TOP <= SCROLL_BOTTOM );

    //NSLog(@"SCROLL-DOWN[%d-%d]",SCROLL_TOP,SCROLL_BOTTOM);
    idx=[self getIndexAtX:0 Y:SCROLL_TOP withPadding:YES];
    if(newLineString == nil)
	newLineString = [[self attrString:@"\n" ascii:YES] retain];
    [_textBuffer insertAttributedString: newLineString atIndex:idx];
    
    if (SCROLL_BOTTOM>=HEIGHT-1) {
        idx=[self getIndexAtX:0 Y:SCROLL_BOTTOM withPadding:YES];
        aRange = NSMakeRange(idx-1, [_textBuffer length]-idx);
        if(aRange.length <= 0)
            aRange.length = 1;
    }
    else {
        idx=[self getIndexAtX:0 Y:SCROLL_BOTTOM withPadding:YES];
        idx2=[self getIndexAtX:0 Y:SCROLL_BOTTOM+1 withPadding:YES];
        aRange = NSMakeRange(idx,idx2-idx);
        if(aRange.length <= 0)
            aRange.length = 1;
    }
    
    [_textBuffer deleteCharactersInRange:aRange];
}

- (void) trimLine: (int) y
{
    int idx,x;
    int i;
    NSString *store=[_textBuffer string];

    idx=[self getIndexAtX:0 Y:y withPadding:YES];
    for(x=0;x<WIDTH&&idx<[store length]&&[store characterAtIndex:idx]!='\n';idx++,x++)
        if (ISDOUBLEWIDTHCHARACTER(idx)) x++;
    for(i=idx;i<[store length]&&[store characterAtIndex:i]!='\n';i++);
    if (i>idx) 
        [_textBuffer deleteCharactersInRange:NSMakeRange(idx,i-idx)];
}    
    
- (void) insertBlank: (int)n
{
    int idx;

    idx=[self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];

    if (CURSOR_IN_MIDDLE) {
        [_textBuffer replaceCharactersInRange:NSMakeRange(idx,1)
                     withAttributedString:[self defaultAttrString:@"??"]];
        idx++;
    }

    if (idx<[_textBuffer length])
        [_textBuffer insertAttributedString:[self attrString:[NSString stringWithCharacters:spaces length:n] ascii:YES] atIndex:idx];
    else
        [_textBuffer appendAttributedString:[self attrString:[NSString stringWithCharacters:spaces length:n] ascii:YES]];

    [self trimLine:CURSOR_Y];
}

- (void) insertLines: (int)n
{    
    int idx, idx2;
    NSRange aRange;
    
    for(;n>0;n--) {
        idx=[self getIndexAtX:0 Y:CURSOR_Y withPadding:YES];
        [_textBuffer insertAttributedString:[self defaultAttrString:@"\n"] atIndex:idx];

        if (SCROLL_BOTTOM<CURSOR_Y||SCROLL_BOTTOM>=HEIGHT-1) {
            idx=[self getIndexAtX:0 Y:SCROLL_BOTTOM withPadding:YES];
            aRange = NSMakeRange(idx-1,[_textBuffer length]-idx);
            if(aRange.length <= 0)
                aRange.length = 1;
        }
        else {
            idx=[self getIndexAtX:0 Y:SCROLL_BOTTOM withPadding:YES];
            idx2=[self getIndexAtX:0 Y:SCROLL_BOTTOM+1 withPadding:YES];
            aRange = NSMakeRange(idx,idx2-idx);
            if(aRange.length <= 0)
                aRange.length = 1;
        }
	
        [_textBuffer deleteCharactersInRange: aRange];
    }
}

- (void) deleteLines: (int)n
{
    int idx, idx2;
    NSRange aRange;

//    NSLog(@"deleteLines %d[%d,%d]",n, CURSOR_X,CURSOR_Y);
    for(;n>0;n--) 
    {
        idx=[self getIndexAtX:0 Y:CURSOR_Y withPadding:YES];
        idx2=[self getIndexAtX:0 Y:CURSOR_Y+1 withPadding:YES];
        aRange = NSMakeRange(idx, idx2-idx);
        if(aRange.length <= 0)
            aRange.length = 1;
        [_textBuffer deleteCharactersInRange:aRange];

        if (SCROLL_BOTTOM<CURSOR_Y||SCROLL_BOTTOM>=HEIGHT-1) {
            [_textBuffer appendAttributedString:[self defaultAttrString:@"\n"]];
        }
        else {
            idx=[self getIndexAtX:0 Y:SCROLL_BOTTOM+1 withPadding:YES];
            [_textBuffer insertAttributedString:[self defaultAttrString:@"\n"] atIndex:idx];
        }
    }
}

- (void)playBell
{
    if (PLAYBELL) 
    {
		NSBeep();
        [_session setBell];
    }
}

- (void)removeOverLine
{
    if (TOP_LINE > scrollbackLines) {
	int idx;
	NSString *s=[_textStorage string];

        int over = TOP_LINE - scrollbackLines;
        int i;

        for(i=0,idx=0;i<over;idx++)
            if ([s characterAtIndex:idx]=='\n') i++;

	[_textStorage beginEditing];
        [_textStorage deleteCharactersInRange:NSMakeRange(0, idx)];
	[_textStorage endEditing];
	
        if (idx<_updateIndex) 
            _updateIndex-=idx;
        else {
            [_textBuffer deleteCharactersInRange:NSMakeRange(0,idx-_updateIndex)];
            _updateIndex=0;
        }
	
        TOP_LINE -= over;

        NSParameterAssert(TOP_LINE >= 0);
    }
}

- (void)deviceReport:(VT100TCC)token
{
    NSData *report = nil;

    if (_shell == nil)
	return;

    switch (token.u.csi.p[0]) {
    case 3: // response from VT100 -- Malfunction -- retry
			break;

    case 5: // Command from host -- Please report status
			report = [TERMINAL reportStatus];
			break;

    case 6: // Command from host -- Please report active position
        {
	    int x, y;

	    if ([TERMINAL originMode]) {
		x = CURSOR_X + 1;
		y = CURSOR_Y - SCROLL_TOP + 1;
	    }
	    else {
		x = CURSOR_X + 1;
		y = CURSOR_Y + 1;
	    }
	    report = [TERMINAL reportActivePositionWithX:x Y:y];
		}
		break;

    case 0: // Response from VT100 -- Ready, No malfuctions detected
    default:
	break;
    }

    if (report != nil) {
	[_shell writeTask:report];
    }
}

- (void)deviceAttribute:(VT100TCC)token
{
    NSData *report = nil;

    if (_shell == nil)
	return;

    report = [TERMINAL reportDeviceAttribute];

    if (report != nil) {
	[_shell writeTask:report];
    }
}

- (NSAttributedString *)attrString:(NSString *)str ascii:(BOOL)asc
{
    NSMutableAttributedString *attr;

    if (str==nil) {
        NSLog(@"attrString: nil received!");
        str=@"";
    }

    attr = [[NSMutableAttributedString alloc]
               initWithString:str
                   attributes:[TERMINAL characterAttributeDictionary:asc]];

    // Mark graphical characters and use our embedded font that has the necessary glyphs
    if(charset[[TERMINAL charset]] && [[ITPreferences sharedInstance] enforceCharacterAlignment])
    {
		[attr addAttribute: NSFontAttributeName value: [NSFont fontWithName:@"FreeMonoBold" size:[[self font] pointSize]] range: NSMakeRange(0, [attr length])];
		[attr addAttribute: @"VT100GraphicalCharacter" value: [NSNumber numberWithInt:1] range: NSMakeRange(0, [attr length])];
    }
    
    [attr autorelease];
    
    return attr;
}

- (NSAttributedString *)defaultAttrString:(NSString *)str
{
    NSMutableAttributedString *attr;

    if (str==nil) {
        NSLog(@"defaultAttrString: nil received!");
        str=@"";
    }
    
    attr = [[NSAttributedString alloc]
               initWithString:str
                   attributes:[TERMINAL defaultCharacterAttributeDictionary:YES]];

    [attr autorelease];

    return attr;
}


- (BOOL) isDoubleWidthCharacter:(unichar)code
{
    BOOL result = NO;

    return (code>=0x1000)?YES:NO; //[NSString isDoubleWidthCharacter:code];

    return result;
}

- (void)blink
{
    if ([self blinkingCursor] && [_session isActiveSession])
    {
        int blinkType;
        NSColor *fg, *bg,*blink;
        NSDictionary *dic;
        NSRange range;
        int len = [[_textStorage string] length];
        int idx = len-1;
        
        [self setScreenLock];
        
        [_textStorage beginEditing];
        for (idx=_updateIndex;idx<len;) 
        {
            blinkType = [[_textStorage attribute:NSBlinkAttributeName atIndex:idx effectiveRange:&range] intValue];
            if (blinkType > 0) 
            {
                for(;idx<range.length+range.location;idx++) 
                {
                    fg = [_textStorage attribute:NSForegroundColorAttributeName atIndex:idx effectiveRange:nil];
                    bg = [_textStorage attribute:NSBackgroundColorAttributeName atIndex:idx effectiveRange:nil];
                    blink = [_textStorage attribute:NSBlinkColorAttributeName atIndex:idx effectiveRange:nil];
                    
                    if (blink == nil) 
                        blink = fg;
                    
                    dic = [NSDictionary dictionaryWithObjectsAndKeys:
                        bg,NSBackgroundColorAttributeName,
                        (blinkShow ? blink:bg),NSForegroundColorAttributeName,
                        blink,NSBlinkColorAttributeName,
                        [NSNumber numberWithInt:1],NSBlinkAttributeName,
                        nil];
                    
                    [_textStorage addAttributes:dic range:NSMakeRange(idx,1)];
                }
            }
            else 
                idx += range.length;
        }
        
        
        idx = [self getTVIndex: CURSOR_X y: CURSOR_Y];
        if ((idx < len) && (idx >= 0))
        {
            fg = [_textStorage attribute:NSForegroundColorAttributeName atIndex:idx effectiveRange:nil];
            bg = [_textStorage attribute:NSBackgroundColorAttributeName atIndex:idx effectiveRange:nil];
                        
            dic=[NSDictionary dictionaryWithObjectsAndKeys:
                (blinkShow?fg:bg),NSBackgroundColorAttributeName,
                (blinkShow?bg:fg),NSForegroundColorAttributeName,
                nil];
            [_textStorage addAttributes:dic range:NSMakeRange(idx,1)];
        }
        
        [_textStorage endEditing];
        blinkShow = !blinkShow;
        [self removeScreenLock];
    }
}

- (int)cursorX
{
    return CURSOR_X+1;
}

- (int) cursorY
{
    return CURSOR_Y+1;
}

- (void) clearTabStop
{
    int i;
    for(i=0;i<300;i++) tabStop[i]=NO;
}

- (NSString *)translate: (NSString *)s
{
    unichar t[3000]; //=malloc(sizeof(char)*[s length]);
    const char *sc=[s UTF8String];
    NSString *ts;
    int i;

    for(i=0;i<strlen(sc);i++) t[i]=charmap[(int)sc[i]];
    ts=[NSString stringWithCharacters:t length:strlen(sc)];

    return ts;
   
}

- (NSMutableAttributedString *) buffer
{
    return _textBuffer;
}


- (int) numberOfLines
{
    int i, len;
    int lineCount = 0;
    NSString *store = [_textStorage string];

    len = [store length];
    if([store length] <= 0)
	return (0);

    lineCount = 1;
    for (i=0; i < len; i++)
    {
	if([store characterAtIndex: i] == '\n')
	    lineCount++;
    }

    return (lineCount);
}

- (void) renewBuffer
{
    NSString *s=[_textBuffer string];
    int len=[s length];
    int idx=len-1;
    int y=0;
    
    for(;y<HEIGHT&&idx>=0;idx--)
    {
        if ([s characterAtIndex:idx]=='\n') 
            y++;
    }
    
    if (y<HEIGHT) 
        idx++; 
    else 
        idx+=2;
    
    if (idx) {
        [_textBuffer deleteCharactersInRange:NSMakeRange(0,idx)];
        _updateIndex+=idx;
    }

    _minIndex=[_textBuffer length];
}

- (void) forceUpdateScreen
{
    _minIndex=0;
    [self updateScreen];
}

- (void)updateScreen
{
    int len, slen;
    int idx;

    idx = [self getIndexAtX:CURSOR_X Y:CURSOR_Y withPadding:YES];
    
    if ([[_session TEXTVIEW] hasMarkedText]) 
    {
        len = idx;
        slen = [[_session TEXTVIEW] markedRange].location;
    }
    else 
    {
        len = [_textBuffer length];
        slen = [_textStorage length];
    }
    
    if (len <= 0 || _minIndex > len)
        return;
    
    [self setScreenLock];

    //NSLog(@"updating: %d, %d, %d, %d",_updateIndex,_minIndex,[_textStorage length],[_textBuffer length]);

    [_textStorage beginEditing];
    
    int replaceLocation = _updateIndex + _minIndex;
    int replaceLength = slen - _updateIndex - _minIndex;
    
    // this was happening, not sure why - so this is a hack fix
    // slen = 1269, _updateIndex = 1269, _minIndex = 15;
    if (replaceLocation < 0)
        replaceLocation = 0;
    if (replaceLength < 0)
        replaceLength = 0;
    
    NSRange charRange = NSMakeRange(replaceLocation, replaceLength);
    
    if (charRange.length && (NSMaxRange(charRange) <= [_textStorage length]))
    {
	[_textStorage replaceCharactersInRange:charRange
		     withAttributedString:[_textBuffer attributedSubstringFromRange:NSMakeRange(_minIndex,len-_minIndex)]];
    }
    else
    {
	[_textStorage appendAttributedString:[_textBuffer attributedSubstringFromRange:NSMakeRange(_minIndex,len-_minIndex)]];
    }
    
    [_textStorage endEditing];

    [self renewBuffer];
    [self removeOverLine];

    cursorIndex = [self getTVIndex:CURSOR_X y:CURSOR_Y];
    [[_session TEXTVIEW] setCursorIndex: cursorIndex];

    [self showCursor];

    [self removeScreenLock];
}

- (void) setScreenAttributes
{
    NSColor *fg, *bg;
   // Change the attributes for the current stuff in the text storage
    if ([TERMINAL screenMode]) {
        bg=[TERMINAL defaultFGColor];
        fg=[TERMINAL defaultBGColor];
    }else {
        fg=[TERMINAL defaultFGColor];
        bg=[TERMINAL defaultBGColor];
    }

    bg = [bg colorWithAlphaComponent: [[_session backgroundColor] alphaComponent]];
    fg = [fg colorWithAlphaComponent: [[_session foregroundColor] alphaComponent]];
    
    [_textBuffer removeAttribute: NSForegroundColorAttributeName
                       range: NSMakeRange(0, [_textBuffer length])];
    [_textBuffer addAttribute: NSForegroundColorAttributeName
                    value: fg
                    range: NSMakeRange(0, [_textBuffer length])];
    [_textBuffer removeAttribute: NSBackgroundColorAttributeName
                       range: NSMakeRange(0, [_textBuffer length])];
    [_textBuffer addAttribute:  NSBackgroundColorAttributeName
                            value: bg
                            range: NSMakeRange(0, [_textBuffer length])];
    [self forceUpdateScreen];
    [[_session scrollView] setBackgroundColor: bg];
    [[_session scrollView] setNeedsDisplay: YES];
}

- (void) setScreenLock
{
    [screenLock lock];
    screenIsLocked = YES;
}

- (void) removeScreenLock
{
    [screenLock unlock];
    screenIsLocked = NO;
}

- (BOOL) screenIsLocked
{
    return screenIsLocked;
}

@end

